package com.jay.video.camera.util

import android.annotation.SuppressLint
import android.media.*
import android.util.Log
import com.jay.media.kit.LogHelper
import java.nio.ByteBuffer

/**
 * @author xuzhou4
 * @date 2022/1/27
 */
class CustomMediaMuxer {

    companion object {
        const val TAG = "CustomMediaMuxer"
        const val DEFAULT_AUDIO_SOURCE = MediaRecorder.AudioSource.MIC
        const val DEFAULT_SAMPLE_RATE = 44100
        const val DEFAULT_CHANNEL_CONFIG = 2
        const val DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT
    }

    private var audioSource = DEFAULT_AUDIO_SOURCE
    private var audioSampleRate = DEFAULT_SAMPLE_RATE
    private var audioChannelCount = DEFAULT_CHANNEL_CONFIG
    private var audioFormat = DEFAULT_AUDIO_FORMAT

    private var mediaMuxer: MediaMuxer? = null

    private lateinit var audioRecord: AudioRecord

    private var bufferSize = 0
    private lateinit var audioByteBuffer: ByteBuffer
    private var audioRecordThread: Thread? = null
    private var timeoutUs = 0L
    private var audioEncodeTimeUs = 0L
    private var audioBufferInfo = MediaCodec.BufferInfo()

    private var videoTrackFormat: MediaFormat? = null
    private var audioTrackFormat: MediaFormat? = null

    fun startRecord(path: String, width: Int, height: Int): Boolean {
        Log.d(TAG, "startRecord() called with: path = $path, width = $width, height = $height")

        audioTrackFormat = MediaFormat.createAudioFormat(
            MediaFormat.MIMETYPE_AUDIO_AAC,
            audioSampleRate,
            audioChannelCount
        )
        videoTrackFormat = MediaFormat.createVideoFormat(
            MediaFormat.MIMETYPE_VIDEO_AVC,
            width,
            height
        )
        mediaMuxer = MediaMuxer(path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
        mediaMuxer?.addTrack(audioTrackFormat!!)
        mediaMuxer?.addTrack(videoTrackFormat!!)
        mediaMuxer?.start()

        audioRecordThread = Thread(audioRecordRunnable)
        audioRecordThread?.start()
        return true
    }

    fun stopRecord() {
        LogHelper.d(TAG, "stop record")
        audioRecord.stop()
    }

    @SuppressLint("MissingPermission")
    private var audioRecordRunnable = Runnable {
        LogHelper.d(TAG, "audio record thread start")

        //准备编码器
        val audioEncoder = createAudioEncoder()

        //开始录音器
        bufferSize = AudioTrack.getMinBufferSize(
            audioSampleRate,
            audioChannelCount,
            audioFormat
        )

        audioByteBuffer = ByteBuffer.allocate(bufferSize)
        audioRecord = AudioRecord(
            audioSource, audioSampleRate, audioChannelCount,
            audioFormat, bufferSize
        )
        audioRecord.startRecording()

        while (audioRecord.recordingState == AudioRecord.RECORDSTATE_RECORDING) {
            //读取到音频数据之后，使用硬编码将数据压缩
            val byteSize = audioRecord.read(audioByteBuffer, bufferSize)
            LogHelper.d(TAG, "audio read size : $byteSize")
            if (byteSize > 0) {
                val dequeueInputBuffer = audioEncoder.dequeueInputBuffer(timeoutUs)
                if (dequeueInputBuffer >= 0) {
                    val inputBuffer = audioEncoder.getInputBuffer(dequeueInputBuffer)
                    inputBuffer?.put(audioByteBuffer)

                    //计算时间
                    audioEncodeTimeUs += (byteSize * 1000.0f / (audioChannelCount * 2 * audioSampleRate)).toLong()

                    audioEncoder.queueInputBuffer(
                        dequeueInputBuffer,
                        0,
                        byteSize,
                        audioEncodeTimeUs,
                        0
                    )
                }

                val dequeueOutputBuffer =
                    audioEncoder.dequeueOutputBuffer(audioBufferInfo, timeoutUs)
                if (dequeueOutputBuffer >= 0) {
                    val outputBuffer = audioEncoder.getOutputBuffer(dequeueOutputBuffer)

                    mediaMuxer?.writeSampleData(0, outputBuffer!!, audioBufferInfo)

                    audioEncoder.releaseOutputBuffer(dequeueOutputBuffer, false)
                } else if (dequeueOutputBuffer == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

                }
            }
        }

        try {
            audioEncoder.stop()
            audioEncoder.release()
        } catch (e: Exception) {
            LogHelper.d(TAG, "audio encoder release fail ${e.message}")
        }

        LogHelper.d(TAG, "audio record thread finish")
    }

    private fun createAudioEncoder(): MediaCodec {
        var audioFormat = MediaFormat.createAudioFormat(
            MediaFormat.MIMETYPE_AUDIO_AAC,
            audioSampleRate,
            audioChannelCount
        )
        audioFormat.setInteger(
            MediaFormat.KEY_AAC_PROFILE,
            MediaCodecInfo.CodecProfileLevel.AACObjectLC
        )
        audioFormat.setInteger(
            MediaFormat.KEY_CHANNEL_MASK,
            MediaCodecInfo.CodecProfileLevel.AACObjectLC
        )
//        audioSampleRate*16 * audioChannelCount
//        audioFormat.setInteger(MediaFormat.KEY_BIT_RATE,)
//        audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE,)
        val encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC)
        encoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        encoder.start()
        return encoder
    }
}